<?php
/**
 * 框架主程序
 * @copyright 2020-2021 WillPHP
 * @author NoMind<24203741@qq.com/113344.com>
 * @version WillPHPv3
 * @since 2021-12-16
 */
namespace wiphp;
defined('WIPHP_URI') or die('Access Denied');
final class App {
	private static $classMap = ['wiphp'=>PATH_CORE, 'app'=>APP_DIR, 'extend'=>PATH_EXTEND, 'addons'=>PATH_ADDONS]; //自动加载路径	
	private static $request = []; //$_GET或$_POST请求参数	
	//运行框架
	public static function start() {
		spl_autoload_register('\wiphp\App::autoload'); //自动加载	
		set_error_handler('\wiphp\App::errorHandler'); //错误处理
		set_exception_handler('\wiphp\App::exceptionHandler'); //异常处理		
		if(!is_dir(APP_PATH.'/controller')) {
			Build::app(); //创建应用
		}
		Config::init(); //配置初始化
		self::parseRequest(); //处理请求
		self::dispatch(); //请求分发
		if (APP_DEBUG) Debug::show(); //显示调试信息
	}	
	//自动加载
	public static function autoload($class) {
		$end = strpos($class, '\\');
		if (false !== $end) {
			$map = substr($class, 0, $end);
			if (isset(self::$classMap[$map])) {
				$fname = substr($class, strlen($map)).'.php';
				$file = strtr(self::$classMap[$map].$fname, '\\', '/');
				if (is_readable($file)) include $file;
			}
		}
		if (APP_DEBUG) Debug::log($class, 'inc');
	}
	//错误处理
	public static function errorHandler($errno, $errstr, $errfile, $errline) {
		$info = '['.date('Y-m-d H:i:s').'] file: '.basename($errfile).' line: '.$errline.' Error: '.$errstr;
		if (APP_DEBUG) {					
			Debug::log($info, 'err');		
			if (self::isAjax()) self::halt($info);
		} else {
			$file = PATH_LOG.'/log_'.APP_NAME.'_'.basename($errfile,'.php').'_'.$errline.'_'.date('Ymd').'.log';
			if (!file_exists($file)) file_put_contents($file, $info);
			if (($errno != E_NOTICE) && ($errno < 2048)) self::halt('Website error,Please contact webmaster.');
		}
	}
	//异常处理
	public static function exceptionHandler($error) {
		$errstr = $error->getMessage();
		$errfile = $error->getFile();
		$errline = $error->getLine();
		$info = '['.date('Y-m-d H:i:s').'] file: '.basename($errfile).' line: '.$errline.' Error: '.$errstr;
		if (APP_DEBUG) {			
			if (self::isAjax()) {
				self::halt($info);
			} else {
				Debug::log($info, 'err');
				Debug::show();
			}
		} else {
			$file = PATH_LOG.'/log_'.APP_NAME.'_'.basename($errfile,'.php').'_'.$errline.'_'.date('Ymd').'.log';
			if (!file_exists($file)) file_put_contents($file, $info);
			self::halt($errstr);
		}
	}	
	//请求处理
	private static function parseRequest() {
		$analysis = Route::parseRoute();	
		
		if (APP_DEBUG) Debug::log('route: '.$analysis['route'], 'other');
		$params = $analysis['param'];
		if (!empty($params)) {
			array_walk_recursive($params, 'self::parseParam');
			self::$request = $params;		
		}		
	}
	//处理请求参数
	private static function parseParam(&$value, $key) {
		$filters = Config::get('filter');
		if (!is_numeric($key) && in_array($key, array_keys($filters))) {
			$fn = $filters[$key];
			if (function_exists($fn)) {
				$value = $fn($value);
			}
		}
		if (!get_magic_quotes_gpc() && !is_array($value)) {
			$value = addslashes(trim($value));
		}
	}	
	//获取页面hash
	public static function getViewHash($route = '', $params = []) {
		if ($route == '') {
			$route = strtolower(__MODULE__.'/'.__ACTION__);
			$params = input();			
		}
		$file = url($route, $params);
		$file = str_replace(__URL__, '', $file);
		if ($file == '/') {
			$file = '/index/index'.URL_SUFFIX;
		}
		return md5($file);
	}
	//显示当前 shtml
	private static function showShtml() {
		$shtml_open = Config::get('app.shtml_open');
		if ($shtml_open && __ACTION__ != 'jump') {
			$vhash = self::getViewHash();
			$sfile = PATH_SHTML.'/'.$vhash.'.shtml';
			$shtml_time = Config::get('app.shtml_time');
			$ftimeok = true;
			if ($shtml_time > 0) {
				$ntime = time();
				$ftime = file_exists($sfile)? filemtime($sfile) : 0;
				$ftimeok = $ftime > ($ntime - $shtml_time);
			}
			if (is_file($sfile) && $ftimeok) {
				include $sfile;				
				if (APP_DEBUG) {
					Debug::log($vhash.'.shtml', 'inc');
					Debug::log('page cache: on (off: shtml_open=&gt;0 [config/app.php])', 'other');
					Debug::show();
				}
				exit();
			}
		}
	}
	//请求分发
	private static function dispatch() {
		self::showShtml(); //显示页面缓存	
		$controller = APP_NAMESPACE.'\\controller\\'.__MODULE__.'Controller';		
		$method = __ACTION__;		
		if (!method_exists($controller, $method)) {	
			$controller = APP_NAMESPACE.'\\controller\\EmptyController';		
			$method = 'empty';
		}		
		if (!method_exists($controller, $method)) self::halt(__MODULE__.'Controller->'.__ACTION__.'() does not exist.');
		$class_method = new \ReflectionMethod($controller, $method);
		if (!$class_method->isPublic()) {
			self::halt(__MODULE__.'Controller->'.$method.'()：The called method is not public.');
		}
		$args = [];
		foreach ($class_method->getParameters() as $arg) {
			$arg_name = $arg->getName();
			$def_val = $arg->isOptional()? $arg->getDefaultValue() : '';
			$args[] = isset(self::$request[$arg_name])? self::$request[$arg_name] : $def_val;
		}
		$res = (new $controller)->$method(...$args);
		if (is_scalar($res)) {
			echo $res;
		} elseif (is_null($res)) {
			return;
		} else {
			header('Content-type: application/json;charset=utf-8');
			echo json_encode($res, JSON_UNESCAPED_UNICODE);
			exit();
		}
	}
	//根据请求参数获到值
	public static function getRequest($name = '', $default = '', $fns = '') {
		$req = self::$request['req'];		
		$val = $default;
		if (empty($name)) {
			$val = $req;
		} elseif (isset($req[$name])) {
			$val = $req[$name];
		}
		if ($fns && !empty($val)) {
			$fns = explode(',', $fns);
			foreach ($fns as $fn) {
				if(function_exists($fn)) {
					$val = is_array($val) ? array_map($fn, $val) : $fn($val);
				}
			}
		}
		return $val;
	}
	//错误提示
	public static function halt($info) {
		if(self::isAjax()){
			$result = ['code' => 500, 'status'=>0, 'msg' => $info];
			exit(json_encode($result, JSON_UNESCAPED_UNICODE));
		} else {
			$html = file_get_contents(WIPHP_PATH.'/tpl/halt.tpl');
			$html = str_replace('__INFO__', $info, $html);
			$html = str_replace('__WIPHP__', __WIPHP__, $html);
			$html = str_replace('__URL__', __URL__, $html);
			exit($html);
		}
	}
	//是否是Ajax提交
	private static function isAjax(){
		if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'){
			return true;
		}
		return false;
	}
}